package org.study.id.other.util;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 分布式ID生成算法：基于Zookeeper
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年04月23日
 */
public class Zookeeper02IdWorker {
    private final String NODE_NAME = "/id-generator";
    private RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    private ExecutorService executorService = Executors.newFixedThreadPool(10);
    private CuratorFramework curatorFrameworkClient;

    public static Zookeeper02IdWorker of(String connectString) {
        return new Zookeeper02IdWorker(connectString);
    }

    private Zookeeper02IdWorker(String connectString) {
        curatorFrameworkClient = CuratorFrameworkFactory.builder()
                .connectString(connectString)
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .build();
        curatorFrameworkClient.start();
        try {
            Stat stat = curatorFrameworkClient.checkExists().forPath(NODE_NAME);
            if (stat == null) {
                curatorFrameworkClient.create().withMode(CreateMode.PERSISTENT).forPath(NODE_NAME, null);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String nextId() {
        String id = null;
        try {
            // 关键点：创建持久顺序节点
            String fullNodeName = curatorFrameworkClient.create()
                    .withMode(CreateMode.PERSISTENT_SEQUENTIAL)
                    .forPath(NODE_NAME, null);
            // 为防止生成的节点浪费系统资源，故生成后异步删除此节点
            executorService.execute(() -> {
                try {
                    curatorFrameworkClient.delete().forPath(fullNodeName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            id = this.delNodeName(fullNodeName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    private String delNodeName(String path) {
        int index = path.lastIndexOf(NODE_NAME);
        if (index >= 0) {
            index += NODE_NAME.length();
            return index <= path.length() ? path.substring(index) : "";
        }
        return path;
    }
}
